"use strict";
layui.define(["layer", "upload", "form"], function (exports) {
  var layer = layui.layer;
  var $ = layui.jquery;
  var upload = layui.upload;
  var form = layui.form;

  // Some variables
  var isMac = /Mac/.test(navigator.platform);
  // Mapping of actions that can be bound to keyboard shortcuts or toolbar buttons
  var bindings = {
    toggleBold: toggleBold,
    toggleItalic: toggleItalic,
    drawLink: drawLink,
    toggleHeadingSmaller: toggleHeadingSmaller,
    toggleHeadingBigger: toggleHeadingBigger,
    drawImage: drawImage,
    toggleBlockquote: toggleBlockquote,
    toggleOrderedList: toggleOrderedList,
    toggleUnorderedList: toggleUnorderedList,
    toggleCodeBlock: toggleCodeBlock,
    togglePreview: togglePreview,
    toggleStrikethrough: toggleStrikethrough,
    toggleHeading1: toggleHeading1,
    toggleHeading2: toggleHeading2,
    toggleHeading3: toggleHeading3,
    cleanBlock: cleanBlock,
    drawTable: drawTable,
    drawHorizontalRule: drawHorizontalRule,
    undo: undo,
    redo: redo,
    toggleSideBySide: toggleSideBySide,
    toggleFullScreen: toggleFullScreen,
  };

  var shortcuts = {
    toggleBold: "Cmd-B",
    toggleItalic: "Cmd-I",
    drawLink: "Cmd-K",
    toggleHeadingSmaller: "Cmd-H",
    toggleHeadingBigger: "Shift-Cmd-H",
    cleanBlock: "Cmd-E",
    drawImage: "Cmd-Alt-I",
    toggleBlockquote: "Cmd-'",
    toggleOrderedList: "Cmd-Alt-L",
    toggleUnorderedList: "Cmd-L",
    toggleCodeBlock: "Cmd-Alt-C",
    togglePreview: "Cmd-P",
    toggleSideBySide: "F9",
    toggleFullScreen: "F11",
  };

  var getBindingName = function (f) {
    for (var key in bindings) {
      if (bindings[key] === f) {
        return key;
      }
    }
    return null;
  };

  var isMobile = function () {
    var check = false;
    (function (a) {
      if (
        /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test(
          a
        ) ||
        /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(
          a.substr(0, 4)
        )
      )
        check = true;
    })(navigator.userAgent || navigator.vendor || window.opera);
    return check;
  };

  /**
   * Fix shortcut. Mac use Command, others use Ctrl.
   */
  function fixShortcut(name) {
    if (isMac) {
      name = name.replace("Ctrl", "Cmd");
    } else {
      name = name.replace("Cmd", "Ctrl");
    }
    return name;
  }

  // Safari, in Private Browsing Mode, looks like it supports localStorage but all calls to setItem throw QuotaExceededError. We're going to detect this and set a variable accordingly.
  function isLocalStorageAvailable() {
    if (typeof localStorage === "object") {
      try {
        localStorage.setItem("smde_localStorage", 1);
        localStorage.removeItem("smde_localStorage");
      } catch (e) {
        return false;
      }
    } else {
      return false;
    }

    return true;
  }

  /**
   * Create icon element for toolbar.
   */
  function createIcon(options, enableTooltips, shortcuts) {
    options = options || {};
    var el = document.createElement("a");
    enableTooltips = enableTooltips == undefined ? true : enableTooltips;

    if (options.title && enableTooltips) {
      el.title = createTootlip(options.title, options.action, shortcuts);

      if (isMac) {
        el.title = el.title.replace("Ctrl", "⌘");
        el.title = el.title.replace("Alt", "⌥");
      }
    }

    el.tabIndex = -1;
    el.className = options.className;
    return el;
  }

  function createSep() {
    var el = document.createElement("i");
    el.className = "separator";
    el.innerHTML = "|";
    return el;
  }

  function createTootlip(title, action, shortcuts) {
    var actionName;
    var tooltip = title;

    if (action) {
      actionName = getBindingName(action);
      if (shortcuts[actionName]) {
        tooltip += " (" + fixShortcut(shortcuts[actionName]) + ")";
      }
    }

    return tooltip;
  }

  /**
   * The state of CodeMirror at the given position.
   */
  function getState(cm, pos) {
    pos = pos || cm.getCursor("start");
    var stat = cm.getTokenAt(pos);
    if (!stat.type) return {};

    var types = stat.type.split(" ");

    var ret = {},
      data,
      text;
    for (var i = 0; i < types.length; i++) {
      data = types[i];
      if (data === "strong") {
        ret.bold = true;
      } else if (data === "variable-2") {
        text = cm.getLine(pos.line);
        if (/^\s*\d+\.\s/.test(text)) {
          ret["ordered-list"] = true;
        } else {
          ret["unordered-list"] = true;
        }
      } else if (data === "atom") {
        ret.quote = true;
      } else if (data === "em") {
        ret.italic = true;
      } else if (data === "quote") {
        ret.quote = true;
      } else if (data === "strikethrough") {
        ret.strikethrough = true;
      } else if (data === "comment") {
        ret.code = true;
      } else if (data === "link") {
        ret.link = true;
      } else if (data === "tag") {
        ret.image = true;
      } else if (data.match(/^header(\-[1-6])?$/)) {
        ret[data.replace("header", "heading")] = true;
      }
    }
    return ret;
  }

  // Saved overflow setting
  var saved_overflow = "";

  /**
   * Toggle full screen of the editor.
   */
  function toggleFullScreen(editor) {
    // Set fullscreen
    var cm = editor.codemirror;
    cm.setOption("fullScreen", !cm.getOption("fullScreen"));

    // Prevent scrolling on body during fullscreen active
    if (cm.getOption("fullScreen")) {
      saved_overflow = document.body.style.overflow;
      document.body.style.overflow = "hidden";
    } else {
      document.body.style.overflow = saved_overflow;
    }

    // Update toolbar class
    var wrap = cm.getWrapperElement();

    if (!/fullscreen/.test(wrap.previousSibling.className)) {
      wrap.previousSibling.className += " fullscreen";
    } else {
      wrap.previousSibling.className = wrap.previousSibling.className.replace(
        /\s*fullscreen\b/,
        ""
      );
    }

    // Update toolbar button
    var toolbarButton = editor.toolbarElements.fullscreen;

    if (!/active/.test(toolbarButton.className)) {
      toolbarButton.className += " active";
    } else {
      toolbarButton.className = toolbarButton.className.replace(
        /\s*active\s*/g,
        ""
      );
    }

    // Hide side by side if needed
    var sidebyside = cm.getWrapperElement().nextSibling;
    if (/editor-preview-active-side/.test(sidebyside.className))
      toggleSideBySide(editor);
  }

  /**
   * Action for toggling bold.
   */
  function toggleBold(editor) {
    _toggleBlock(editor, "bold", editor.options.blockStyles.bold);
  }

  /**
   * Action for toggling italic.
   */
  function toggleItalic(editor) {
    _toggleBlock(editor, "italic", editor.options.blockStyles.italic);
  }

  /**
   * Action for toggling strikethrough.
   */
  function toggleStrikethrough(editor) {
    _toggleBlock(editor, "strikethrough", "~~");
  }

  /**
   * Action for toggling code block.
   */
  function toggleCodeBlock(editor) {
    var fenceCharsToInsert = editor.options.blockStyles.code;

    function fencing_line(line) {
      /* return true, if this is a ``` or ~~~ line */
      if (typeof line !== "object") {
        throw (
          "fencing_line() takes a 'line' object (not a line number, or line text).  Got: " +
          typeof line +
          ": " +
          line
        );
      }
      return (
        line.styles &&
        line.styles[2] &&
        line.styles[2].indexOf("formatting-code-block") !== -1
      );
    }

    function token_state(token) {
      // base goes an extra level deep when mode backdrops are used, e.g. spellchecker on
      return token.state.base.base || token.state.base;
    }

    function code_type(cm, line_num, line, firstTok, lastTok) {
      /*
       * Return "single", "indented", "fenced" or false
       *
       * cm and line_num are required.  Others are optional for efficiency
       *   To check in the middle of a line, pass in firstTok yourself.
       */
      line = line || cm.getLineHandle(line_num);
      firstTok =
        firstTok ||
        cm.getTokenAt({
          line: line_num,
          ch: 1,
        });
      lastTok =
        lastTok ||
        (!!line.text &&
          cm.getTokenAt({
            line: line_num,
            ch: line.text.length - 1,
          }));
      var types = firstTok.type ? firstTok.type.split(" ") : [];
      if (lastTok && token_state(lastTok).indentedCode) {
        // have to check last char, since first chars of first line aren"t marked as indented
        return "indented";
      } else if (types.indexOf("comment") === -1) {
        // has to be after "indented" check, since first chars of first indented line aren"t marked as such
        return false;
      } else if (
        token_state(firstTok).fencedChars ||
        token_state(lastTok).fencedChars ||
        fencing_line(line)
      ) {
        return "fenced";
      } else {
        return "single";
      }
    }

    function insertFencingAtSelection(
      cm,
      cur_start,
      cur_end,
      fenceCharsToInsert
    ) {
      var start_line_sel = cur_start.line + 1,
        end_line_sel = cur_end.line + 1,
        sel_multi = cur_start.line !== cur_end.line,
        repl_start = fenceCharsToInsert + "\n",
        repl_end = "\n" + fenceCharsToInsert;
      if (sel_multi) {
        end_line_sel++;
      }
      // handle last char including \n or not
      if (sel_multi && cur_end.ch === 0) {
        repl_end = fenceCharsToInsert + "\n";
        end_line_sel--;
      }
      _replaceSelection(cm, false, [repl_start, repl_end]);
      cm.setSelection(
        {
          line: start_line_sel,
          ch: 0,
        },
        {
          line: end_line_sel,
          ch: 0,
        }
      );
    }

    var cm = editor.codemirror,
      cur_start = cm.getCursor("start"),
      cur_end = cm.getCursor("end"),
      tok = cm.getTokenAt({
        line: cur_start.line,
        ch: cur_start.ch || 1,
      }), // avoid ch 0 which is a cursor pos but not token
      line = cm.getLineHandle(cur_start.line),
      is_code = code_type(cm, cur_start.line, line, tok);
    var block_start, block_end, lineCount;

    if (is_code === "single") {
      // similar to some easymde _toggleBlock logic
      var start = line.text.slice(0, cur_start.ch).replace("`", ""),
        end = line.text.slice(cur_start.ch).replace("`", "");
      cm.replaceRange(
        start + end,
        {
          line: cur_start.line,
          ch: 0,
        },
        {
          line: cur_start.line,
          ch: 99999999999999,
        }
      );
      cur_start.ch--;
      if (cur_start !== cur_end) {
        cur_end.ch--;
      }
      cm.setSelection(cur_start, cur_end);
      cm.focus();
    } else if (is_code === "fenced") {
      if (cur_start.line !== cur_end.line || cur_start.ch !== cur_end.ch) {
        // use selection

        // find the fenced line so we know what type it is (tilde, backticks, number of them)
        for (block_start = cur_start.line; block_start >= 0; block_start--) {
          line = cm.getLineHandle(block_start);
          if (fencing_line(line)) {
            break;
          }
        }
        var fencedTok = cm.getTokenAt({
          line: block_start,
          ch: 1,
        });
        var fence_chars = token_state(fencedTok).fencedChars;
        var start_text, start_line;
        var end_text, end_line;
        // check for selection going up against fenced lines, in which case we don't want to add more fencing
        if (fencing_line(cm.getLineHandle(cur_start.line))) {
          start_text = "";
          start_line = cur_start.line;
        } else if (fencing_line(cm.getLineHandle(cur_start.line - 1))) {
          start_text = "";
          start_line = cur_start.line - 1;
        } else {
          start_text = fence_chars + "\n";
          start_line = cur_start.line;
        }
        if (fencing_line(cm.getLineHandle(cur_end.line))) {
          end_text = "";
          end_line = cur_end.line;
          if (cur_end.ch === 0) {
            end_line += 1;
          }
        } else if (
          cur_end.ch !== 0 &&
          fencing_line(cm.getLineHandle(cur_end.line + 1))
        ) {
          end_text = "";
          end_line = cur_end.line + 1;
        } else {
          end_text = fence_chars + "\n";
          end_line = cur_end.line + 1;
        }
        if (cur_end.ch === 0) {
          // full last line selected, putting cursor at beginning of next
          end_line -= 1;
        }
        cm.operation(function () {
          // end line first, so that line numbers don't change
          cm.replaceRange(
            end_text,
            {
              line: end_line,
              ch: 0,
            },
            {
              line: end_line + (end_text ? 0 : 1),
              ch: 0,
            }
          );
          cm.replaceRange(
            start_text,
            {
              line: start_line,
              ch: 0,
            },
            {
              line: start_line + (start_text ? 0 : 1),
              ch: 0,
            }
          );
        });
        cm.setSelection(
          {
            line: start_line + (start_text ? 1 : 0),
            ch: 0,
          },
          {
            line: end_line + (start_text ? 1 : -1),
            ch: 0,
          }
        );
        cm.focus();
      } else {
        // no selection, search for ends of this fenced block
        var search_from = cur_start.line;
        if (fencing_line(cm.getLineHandle(cur_start.line))) {
          // gets a little tricky if cursor is right on a fenced line
          if (code_type(cm, cur_start.line + 1) === "fenced") {
            block_start = cur_start.line;
            search_from = cur_start.line + 1; // for searching for "end"
          } else {
            block_end = cur_start.line;
            search_from = cur_start.line - 1; // for searching for "start"
          }
        }
        if (block_start === undefined) {
          for (block_start = search_from; block_start >= 0; block_start--) {
            line = cm.getLineHandle(block_start);
            if (fencing_line(line)) {
              break;
            }
          }
        }
        if (block_end === undefined) {
          lineCount = cm.lineCount();
          for (block_end = search_from; block_end < lineCount; block_end++) {
            line = cm.getLineHandle(block_end);
            if (fencing_line(line)) {
              break;
            }
          }
        }
        cm.operation(function () {
          cm.replaceRange(
            "",
            {
              line: block_start,
              ch: 0,
            },
            {
              line: block_start + 1,
              ch: 0,
            }
          );
          cm.replaceRange(
            "",
            {
              line: block_end - 1,
              ch: 0,
            },
            {
              line: block_end,
              ch: 0,
            }
          );
        });
        cm.focus();
      }
    } else if (is_code === "indented") {
      if (cur_start.line !== cur_end.line || cur_start.ch !== cur_end.ch) {
        // use selection
        block_start = cur_start.line;
        block_end = cur_end.line;
        if (cur_end.ch === 0) {
          block_end--;
        }
      } else {
        // no selection, search for ends of this indented block
        for (block_start = cur_start.line; block_start >= 0; block_start--) {
          line = cm.getLineHandle(block_start);
          if (line.text.match(/^\s*$/)) {
            // empty or all whitespace - keep going
            continue;
          } else {
            if (code_type(cm, block_start, line) !== "indented") {
              block_start += 1;
              break;
            }
          }
        }
        lineCount = cm.lineCount();
        for (block_end = cur_start.line; block_end < lineCount; block_end++) {
          line = cm.getLineHandle(block_end);
          if (line.text.match(/^\s*$/)) {
            // empty or all whitespace - keep going
            continue;
          } else {
            if (code_type(cm, block_end, line) !== "indented") {
              block_end -= 1;
              break;
            }
          }
        }
      }
      // if we are going to un-indent based on a selected set of lines, and the next line is indented too, we need to
      // insert a blank line so that the next line(s) continue to be indented code
      var next_line = cm.getLineHandle(block_end + 1),
        next_line_last_tok =
          next_line &&
          cm.getTokenAt({
            line: block_end + 1,
            ch: next_line.text.length - 1,
          }),
        next_line_indented =
          next_line_last_tok && token_state(next_line_last_tok).indentedCode;
      if (next_line_indented) {
        cm.replaceRange("\n", {
          line: block_end + 1,
          ch: 0,
        });
      }

      for (var i = block_start; i <= block_end; i++) {
        cm.indentLine(i, "subtract"); // TODO: this doesn't get tracked in the history, so can't be undone :(
      }
      cm.focus();
    } else {
      // insert code formatting
      var no_sel_and_starting_of_line =
        cur_start.line === cur_end.line &&
        cur_start.ch === cur_end.ch &&
        cur_start.ch === 0;
      var sel_multi = cur_start.line !== cur_end.line;
      if (no_sel_and_starting_of_line || sel_multi) {
        insertFencingAtSelection(cm, cur_start, cur_end, fenceCharsToInsert);
      } else {
        _replaceSelection(cm, false, ["`", "`"]);
      }
    }
  }

  /**
   * Action for toggling blockquote.
   */
  function toggleBlockquote(editor) {
    var cm = editor.codemirror;
    _toggleLine(cm, "quote");
  }

  /**
   * Action for toggling heading size: normal -> h1 -> h2 -> h3 -> h4 -> h5 -> h6 -> normal
   */
  function toggleHeadingSmaller(editor) {
    var cm = editor.codemirror;
    _toggleHeading(cm, "smaller");
  }

  /**
   * Action for toggling heading size: normal -> h6 -> h5 -> h4 -> h3 -> h2 -> h1 -> normal
   */
  function toggleHeadingBigger(editor) {
    var cm = editor.codemirror;
    _toggleHeading(cm, "bigger");
  }

  /**
   * Action for toggling heading size 1
   */
  function toggleHeading1(editor) {
    var cm = editor.codemirror;
    _toggleHeading(cm, undefined, 1);
  }

  /**
   * Action for toggling heading size 2
   */
  function toggleHeading2(editor) {
    var cm = editor.codemirror;
    _toggleHeading(cm, undefined, 2);
  }

  /**
   * Action for toggling heading size 3
   */
  function toggleHeading3(editor) {
    var cm = editor.codemirror;
    _toggleHeading(cm, undefined, 3);
  }

  /**
   * Action for toggling ul.
   */
  function toggleUnorderedList(editor) {
    var cm = editor.codemirror;
    _toggleLine(cm, "unordered-list");
  }

  /**
   * Action for toggling ol.
   */
  function toggleOrderedList(editor) {
    var cm = editor.codemirror;
    _toggleLine(cm, "ordered-list");
  }

  /**
   * Action for clean block (remove headline, list, blockquote code, markers)
   */
  function cleanBlock(editor) {
    var cm = editor.codemirror;
    _cleanBlock(cm);
  }

  /**
   * Action for drawing a link.
   */
  function drawLink(editor) {
    var cm = editor.codemirror;
    var stat = getState(cm);
    var options = editor.options;
    var edit = $(".editor-toolbar > .fa-link");
    //超链接
    layer.prompt(
      {
        title: options.promptTexts.link,
        shadeClose: true,
        fixed: false,
        id: "LAY_flyedit_href",
        offset: [
          edit.offset().top + 40 + "px",
          edit.offset().left - 120 + "px",
        ],
      },
      function (val, index, elem) {
        if (!/^http(s*):\/\/[\S]/.test(val)) {
          layer.tips("这根本不是个链接，不要骗我。", elem, {
            tips: 1,
          });
          return;
        }
        _replaceSelection(cm, stat.link, options.insertTexts.link, val);
        layer.close(index);
      }
    );
  }

  /**
   * Action for drawing an img.
   */
  function drawImage(editor) {
    var cm = editor.codemirror;
    var stat = getState(cm);
    var options = editor.options;
    var uploadParam = options.upload || {};
    var response = uploadParam.response || {};
    var edit = $(".editor-toolbar >  .fa-picture-o");
    //插入图片
    layer.open({
      type: 1,
      id: "fly-jie-upload",
      title: "插入图片",
      area: "auto",
      shadeClose: true,
      area: "465px",
      fixed: false,
      offset: [edit.offset().top + 40 + "px", edit.offset().left - 120 + "px"],
      skin: "layui-layer-border",
      content: [
        '<ul class="layui-form layui-form-pane" style="margin: 20px;">',
        '<li class="layui-form-item">',
        '<label class="layui-form-label">URL</label>',
        '<div class="layui-input-inline">',
        '<input required name="image" placeholder="支持直接粘贴远程图片地址" value="" class="layui-input">',
        "</div>",
        '<button type="button" class="layui-btn layui-btn-primary" id="uploadImg"><i class="layui-icon">&#xe67c;</i>上传图片</button>',
        "</li>",
        '<li class="layui-form-item" style="text-align: center;">',
        '<button type="button" lay-submit lay-filter="uploadImages" class="layui-btn">确认</button>',
        "</li>",
        "</ul>",
      ].join(""),
      success: function (layero, index) {
        var image = layero.find('input[name="image"]');
        console.log(uploadParam);

        //执行上传实例
        upload.render({
          elem: "#uploadImg",
          url: uploadParam.url || "/file/upload",
          size: uploadParam.size || 200,
          done: function (res) {
            if (
              res[response.status || "status"] == response.successStatus ||
              200
            ) {
              image.val(res[response.url || "url"]);
            } else {
              layer.msg(res[response.msg] || response.msg || "图片上传失败", {
                icon: 5,
              });
            }
          },
        });

        form.on("submit(uploadImages)", function (data) {
          var field = data.field;
          if (!field.image) return image.focus();
          _replaceSelection(
            cm,
            stat.image,
            options.insertTexts.image,
            field.image
          );
          layer.close(index);
        });
      },
    });
  }

  /**
   * Action for drawing a table.
   */
  function drawTable(editor) {
    var cm = editor.codemirror;
    var stat = getState(cm);
    var options = editor.options;
    _replaceSelection(cm, stat.table, options.insertTexts.table);
  }

  /**
   * Action for drawing a horizontal rule.
   */
  function drawHorizontalRule(editor) {
    var cm = editor.codemirror;
    var stat = getState(cm);
    var options = editor.options;
    _replaceSelection(cm, stat.image, options.insertTexts.horizontalRule);
  }

  /**
   * Undo action.
   */
  function undo(editor) {
    var cm = editor.codemirror;
    cm.undo();
    cm.focus();
  }

  /**
   * Redo action.
   */
  function redo(editor) {
    var cm = editor.codemirror;
    cm.redo();
    cm.focus();
  }

  /**
   * Toggle side by side preview
   */
  function toggleSideBySide(editor) {
    var cm = editor.codemirror;
    var wrapper = cm.getWrapperElement();
    var preview = wrapper.nextSibling;
    var toolbarButton = editor.toolbarElements["side-by-side"];
    var useSideBySideListener = false;
    if (/editor-preview-active-side/.test(preview.className)) {
      preview.className = preview.className.replace(
        /\s*editor-preview-active-side\s*/g,
        ""
      );
      toolbarButton.className = toolbarButton.className.replace(
        /\s*active\s*/g,
        ""
      );
      wrapper.className = wrapper.className.replace(
        /\s*CodeMirror-sided\s*/g,
        " "
      );
    } else {
      // When the preview button is clicked for the first time,
      // give some time for the transition from editor.css to fire and the view to slide from right to left,
      // instead of just appearing.
      setTimeout(function () {
        if (!cm.getOption("fullScreen")) toggleFullScreen(editor);
        preview.className += " editor-preview-active-side";
      }, 1);
      toolbarButton.className += " active";
      wrapper.className += " CodeMirror-sided";
      useSideBySideListener = true;
    }

    // Hide normal preview if active
    var previewNormal = wrapper.lastChild;
    if (/editor-preview-active/.test(previewNormal.className)) {
      previewNormal.className = previewNormal.className.replace(
        /\s*editor-preview-active\s*/g,
        ""
      );
      var toolbar = editor.toolbarElements.preview;
      var toolbar_div = wrapper.previousSibling;
      toolbar.className = toolbar.className.replace(/\s*active\s*/g, "");
      toolbar_div.className = toolbar_div.className.replace(
        /\s*disabled-for-preview*/g,
        ""
      );
    }

    var sideBySideRenderingFunction = function () {
      preview.innerHTML = editor.options.previewRender(editor.value(), preview);
    };

    if (!cm.sideBySideRenderingFunction) {
      cm.sideBySideRenderingFunction = sideBySideRenderingFunction;
    }

    if (useSideBySideListener) {
      preview.innerHTML = editor.options.previewRender(editor.value(), preview);
      cm.on("update", cm.sideBySideRenderingFunction);
    } else {
      cm.off("update", cm.sideBySideRenderingFunction);
    }

    // Refresh to fix selection being off (#309)
    cm.refresh();
  }

  /**
   * Preview action.
   */
  function togglePreview(editor) {
    var cm = editor.codemirror;
    var wrapper = cm.getWrapperElement();
    var toolbar_div = wrapper.previousSibling;
    var toolbar = editor.options.toolbar
      ? editor.toolbarElements.preview
      : false;
    var preview = wrapper.lastChild;
    if (!preview || !/editor-preview/.test(preview.className)) {
      preview = document.createElement("div");
      preview.className = "editor-preview markdown-body";
      wrapper.appendChild(preview);
    }
    if (/editor-preview-active/.test(preview.className)) {
      preview.className = preview.className.replace(
        /\s*editor-preview-active\s*/g,
        ""
      );
      if (toolbar) {
        toolbar.className = toolbar.className.replace(/\s*active\s*/g, "");
        toolbar_div.className = toolbar_div.className.replace(
          /\s*disabled-for-preview*/g,
          ""
        );
      }
    } else {
      // When the preview button is clicked for the first time,
      // give some time for the transition from editor.css to fire and the view to slide from right to left,
      // instead of just appearing.
      setTimeout(function () {
        preview.className += " editor-preview-active";
      }, 1);
      if (toolbar) {
        toolbar.className += " active";
        toolbar_div.className += " disabled-for-preview";
      }
    }
    preview.innerHTML = editor.options.previewRender(editor.value(), preview);

    // Turn off side by side if needed
    var sidebyside = cm.getWrapperElement().nextSibling;
    if (/editor-preview-active-side/.test(sidebyside.className))
      toggleSideBySide(editor);
  }

  function _replaceSelection(cm, active, startEnd, url) {
    if (
      /editor-preview-active/.test(cm.getWrapperElement().lastChild.className)
    )
      return;

    var text;
    var start = startEnd[0];
    var end = startEnd[1];
    var startPoint = cm.getCursor("start");
    var endPoint = cm.getCursor("end");
    if (url) {
      end = end.replace("#url#", url);
    }
    if (active) {
      text = cm.getLine(startPoint.line);
      start = text.slice(0, startPoint.ch);
      end = text.slice(startPoint.ch);
      cm.replaceRange(start + end, {
        line: startPoint.line,
        ch: 0,
      });
    } else {
      text = cm.getSelection();
      cm.replaceSelection(start + text + end);

      startPoint.ch += start.length;
      if (startPoint !== endPoint) {
        endPoint.ch += start.length;
      }
    }
    cm.setSelection(startPoint, endPoint);
    cm.focus();
  }

  function _toggleHeading(cm, direction, size) {
    if (
      /editor-preview-active/.test(cm.getWrapperElement().lastChild.className)
    )
      return;

    var startPoint = cm.getCursor("start");
    var endPoint = cm.getCursor("end");
    for (var i = startPoint.line; i <= endPoint.line; i++) {
      (function (i) {
        var text = cm.getLine(i);
        var currHeadingLevel = text.search(/[^#]/);

        if (direction !== undefined) {
          if (currHeadingLevel <= 0) {
            if (direction == "bigger") {
              text = "###### " + text;
            } else {
              text = "# " + text;
            }
          } else if (currHeadingLevel == 6 && direction == "smaller") {
            text = text.substr(7);
          } else if (currHeadingLevel == 1 && direction == "bigger") {
            text = text.substr(2);
          } else {
            if (direction == "bigger") {
              text = text.substr(1);
            } else {
              text = "#" + text;
            }
          }
        } else {
          if (size == 1) {
            if (currHeadingLevel <= 0) {
              text = "# " + text;
            } else if (currHeadingLevel == size) {
              text = text.substr(currHeadingLevel + 1);
            } else {
              text = "# " + text.substr(currHeadingLevel + 1);
            }
          } else if (size == 2) {
            if (currHeadingLevel <= 0) {
              text = "## " + text;
            } else if (currHeadingLevel == size) {
              text = text.substr(currHeadingLevel + 1);
            } else {
              text = "## " + text.substr(currHeadingLevel + 1);
            }
          } else {
            if (currHeadingLevel <= 0) {
              text = "### " + text;
            } else if (currHeadingLevel == size) {
              text = text.substr(currHeadingLevel + 1);
            } else {
              text = "### " + text.substr(currHeadingLevel + 1);
            }
          }
        }

        cm.replaceRange(
          text,
          {
            line: i,
            ch: 0,
          },
          {
            line: i,
            ch: 99999999999999,
          }
        );
      })(i);
    }
    cm.focus();
  }

  function _toggleLine(cm, name) {
    if (
      /editor-preview-active/.test(cm.getWrapperElement().lastChild.className)
    )
      return;

    var stat = getState(cm);
    var startPoint = cm.getCursor("start");
    var endPoint = cm.getCursor("end");
    var repl = {
      quote: /^(\s*)\>\s+/,
      "unordered-list": /^(\s*)(\*|\-|\+)\s+/,
      "ordered-list": /^(\s*)\d+\.\s+/,
    };
    var map = {
      quote: "> ",
      "unordered-list": "* ",
      "ordered-list": "1. ",
    };
    for (var i = startPoint.line; i <= endPoint.line; i++) {
      (function (i) {
        var text = cm.getLine(i);
        if (stat[name]) {
          text = text.replace(repl[name], "$1");
        } else {
          text = map[name] + text;
        }
        cm.replaceRange(
          text,
          {
            line: i,
            ch: 0,
          },
          {
            line: i,
            ch: 99999999999999,
          }
        );
      })(i);
    }
    cm.focus();
  }

  function _toggleBlock(editor, type, start_chars, end_chars) {
    if (
      /editor-preview-active/.test(
        editor.codemirror.getWrapperElement().lastChild.className
      )
    )
      return;

    end_chars = typeof end_chars === "undefined" ? start_chars : end_chars;
    var cm = editor.codemirror;
    var stat = getState(cm);

    var text;
    var start = start_chars;
    var end = end_chars;

    var startPoint = cm.getCursor("start");
    var endPoint = cm.getCursor("end");

    if (stat[type]) {
      text = cm.getLine(startPoint.line);
      start = text.slice(0, startPoint.ch);
      end = text.slice(startPoint.ch);
      if (type == "bold") {
        start = start.replace(/(\*\*|__)(?![\s\S]*(\*\*|__))/, "");
        end = end.replace(/(\*\*|__)/, "");
      } else if (type == "italic") {
        start = start.replace(/(\*|_)(?![\s\S]*(\*|_))/, "");
        end = end.replace(/(\*|_)/, "");
      } else if (type == "strikethrough") {
        start = start.replace(/(\*\*|~~)(?![\s\S]*(\*\*|~~))/, "");
        end = end.replace(/(\*\*|~~)/, "");
      }
      cm.replaceRange(
        start + end,
        {
          line: startPoint.line,
          ch: 0,
        },
        {
          line: startPoint.line,
          ch: 99999999999999,
        }
      );

      if (type == "bold" || type == "strikethrough") {
        startPoint.ch -= 2;
        if (startPoint !== endPoint) {
          endPoint.ch -= 2;
        }
      } else if (type == "italic") {
        startPoint.ch -= 1;
        if (startPoint !== endPoint) {
          endPoint.ch -= 1;
        }
      }
    } else {
      text = cm.getSelection();
      if (type == "bold") {
        text = text.split("**").join("");
        text = text.split("__").join("");
      } else if (type == "italic") {
        text = text.split("*").join("");
        text = text.split("_").join("");
      } else if (type == "strikethrough") {
        text = text.split("~~").join("");
      }
      cm.replaceSelection(start + text + end);

      startPoint.ch += start_chars.length;
      endPoint.ch = startPoint.ch + text.length;
    }

    cm.setSelection(startPoint, endPoint);
    cm.focus();
  }

  function _cleanBlock(cm) {
    if (
      /editor-preview-active/.test(cm.getWrapperElement().lastChild.className)
    )
      return;

    var startPoint = cm.getCursor("start");
    var endPoint = cm.getCursor("end");
    var text;

    for (var line = startPoint.line; line <= endPoint.line; line++) {
      text = cm.getLine(line);
      text = text.replace(/^[ ]*([# ]+|\*|\-|[> ]+|[0-9]+(.|\)))[ ]*/, "");

      cm.replaceRange(
        text,
        {
          line: line,
          ch: 0,
        },
        {
          line: line,
          ch: 99999999999999,
        }
      );
    }
  }

  // Merge the properties of one object into another.
  function _mergeProperties(target, source) {
    for (var property in source) {
      if (source.hasOwnProperty(property)) {
        if (source[property] instanceof Array) {
          target[property] = source[property].concat(
            target[property] instanceof Array ? target[property] : []
          );
        } else if (
          source[property] !== null &&
          typeof source[property] === "object" &&
          source[property].constructor === Object
        ) {
          target[property] = _mergeProperties(
            target[property] || {},
            source[property]
          );
        } else {
          target[property] = source[property];
        }
      }
    }

    return target;
  }

  // Merge an arbitrary number of objects into one.
  function extend(target) {
    for (var i = 1; i < arguments.length; i++) {
      target = _mergeProperties(target, arguments[i]);
    }

    return target;
  }

  /* The right word count in respect for CJK. */
  function wordCount(data) {
    var pattern = /[a-zA-Z0-9_\u0392-\u03c9\u0410-\u04F9]+|[\u4E00-\u9FFF\u3400-\u4dbf\uf900-\ufaff\u3040-\u309f\uac00-\ud7af]+/g;
    var m = data.match(pattern);
    var count = 0;
    if (m === null) return count;
    for (var i = 0; i < m.length; i++) {
      if (m[i].charCodeAt(0) >= 0x4e00) {
        count += m[i].length;
      } else {
        count += 1;
      }
    }
    return count;
  }

  var toolbarBuiltInButtons = {
    bold: {
      name: "bold",
      action: toggleBold,
      className: "fa fa-bold",
      title: "黑体",
      default: true,
    },
    italic: {
      name: "italic",
      action: toggleItalic,
      className: "fa fa-italic",
      title: "斜体",
      default: true,
    },
    strikethrough: {
      name: "strikethrough",
      action: toggleStrikethrough,
      className: "fa fa-strikethrough",
      title: "删除线",
    },
    heading: {
      name: "heading",
      action: toggleHeadingSmaller,
      className: "fa fa-header",
      title: "标题",
      default: true,
    },
    "heading-smaller": {
      name: "heading-smaller",
      action: toggleHeadingSmaller,
      className: "fa fa-header fa-header-x fa-header-smaller",
      title: "缩小标题",
    },
    "heading-bigger": {
      name: "heading-bigger",
      action: toggleHeadingBigger,
      className: "fa fa-header fa-header-x fa-header-bigger",
      title: "增大标题",
    },
    "heading-1": {
      name: "heading-1",
      action: toggleHeading1,
      className: "fa fa-header fa-header-x fa-header-1",
      title: "大标题",
    },
    "heading-2": {
      name: "heading-2",
      action: toggleHeading2,
      className: "fa fa-header fa-header-x fa-header-2",
      title: "中标题",
    },
    "heading-3": {
      name: "heading-3",
      action: toggleHeading3,
      className: "fa fa-header fa-header-x fa-header-3",
      title: "小标题",
    },
    "separator-1": {
      name: "separator-1",
    },
    code: {
      name: "code",
      action: toggleCodeBlock,
      className: "fa fa-code",
      title: "代码块",
    },
    quote: {
      name: "quote",
      action: toggleBlockquote,
      className: "fa fa-quote-left",
      title: "引用",
      default: true,
    },
    "unordered-list": {
      name: "unordered-list",
      action: toggleUnorderedList,
      className: "fa fa-list-ul",
      title: "无序列表",
      default: true,
    },
    "ordered-list": {
      name: "ordered-list",
      action: toggleOrderedList,
      className: "fa fa-list-ol",
      title: "有序列表",
      default: true,
    },
    "clean-block": {
      name: "clean-block",
      action: cleanBlock,
      className: "fa fa-eraser fa-clean-block",
      title: "清除块",
    },
    "separator-2": {
      name: "separator-2",
    },
    link: {
      name: "link",
      action: drawLink,
      className: "fa fa-link",
      title: "超链接",
      default: true,
    },
    image: {
      name: "image",
      action: drawImage,
      className: "fa fa-picture-o",
      title: "图片",
      default: true,
    },
    table: {
      name: "table",
      action: drawTable,
      className: "fa fa-table",
      title: "表格",
    },
    "horizontal-rule": {
      name: "horizontal-rule",
      action: drawHorizontalRule,
      className: "fa fa-minus",
      title: "水平线",
    },
    "separator-3": {
      name: "separator-3",
    },
    preview: {
      name: "preview",
      action: togglePreview,
      className: "fa fa-eye no-disable",
      title: "预览",
      default: true,
    },
    "side-by-side": {
      name: "side-by-side",
      action: toggleSideBySide,
      className: "fa fa-columns no-disable no-mobile",
      title: "分屏预览",
      default: true,
    },
    fullscreen: {
      name: "fullscreen",
      action: toggleFullScreen,
      className: "fa fa-arrows-alt no-disable no-mobile",
      title: "全屏",
      default: true,
    },
    "separator-4": {
      name: "separator-4",
    },
    guide: {
      name: "guide",
      action: "https://mde.layjava.com",
      className: "fa fa-question-circle",
      title: "说明",
      default: true,
    },
    "separator-5": {
      name: "separator-5",
    },
    undo: {
      name: "undo",
      action: undo,
      className: "fa fa-undo no-disable",
      title: "清空",
    },
    redo: {
      name: "redo",
      action: redo,
      className: "fa fa-repeat no-disable",
      title: "恢复",
    },
  };

  var insertTexts = {
    link: ["[", "](#url#)"],
    image: ["![](", "#url#)"],
    table: [
      "",
      "\n\n| Column 1 | Column 2 | Column 3 |\n| -------- | -------- | -------- |\n| Text     | Text     | Text     |\n\n",
    ],
    horizontalRule: ["", "\n\n-----\n\n"],
  };

  var promptTexts = {
    link: "请输入/粘贴超链接URL地址",
    image: "请输入/粘贴图片URL地址",
  };

  var blockStyles = {
    bold: "**",
    code: "```",
    italic: "*",
  };

  var easymde = {
    /**
     * Bind static methods for exports.
     */
    toggleBold: toggleBold,
    toggleItalic: toggleItalic,
    toggleStrikethrough: toggleStrikethrough,
    toggleBlockquote: toggleBlockquote,
    toggleHeadingSmaller: toggleHeadingSmaller,
    toggleHeadingBigger: toggleHeadingBigger,
    toggleHeading1: toggleHeading1,
    toggleHeading2: toggleHeading2,
    toggleHeading3: toggleHeading3,
    toggleCodeBlock: toggleCodeBlock,
    toggleUnorderedList: toggleUnorderedList,
    toggleOrderedList: toggleOrderedList,
    cleanBlock: cleanBlock,
    drawLink: drawLink,
    drawImage: drawImage,
    drawTable: drawTable,
    drawHorizontalRule: drawHorizontalRule,
    undo: undo,
    redo: redo,
    togglePreview: togglePreview,
    toggleSideBySide: toggleSideBySide,
    toggleFullScreen: toggleFullScreen,
    /**
     * Bind instance methods for exports.
     */
    toggleBold: function () {
      toggleBold(this);
    },
    toggleItalic: function () {
      toggleItalic(this);
    },
    toggleStrikethrough: function () {
      toggleStrikethrough(this);
    },
    toggleBlockquote: function () {
      toggleBlockquote(this);
    },
    toggleHeadingSmaller: function () {
      toggleHeadingSmaller(this);
    },
    toggleHeadingBigger: function () {
      toggleHeadingBigger(this);
    },
    toggleHeading1: function () {
      toggleHeading1(this);
    },
    toggleHeading2: function () {
      toggleHeading2(this);
    },
    toggleHeading3: function () {
      toggleHeading3(this);
    },
    toggleCodeBlock: function () {
      toggleCodeBlock(this);
    },
    toggleUnorderedList: function () {
      toggleUnorderedList(this);
    },
    toggleOrderedList: function () {
      toggleOrderedList(this);
    },
    cleanBlock: function () {
      cleanBlock(this);
    },
    drawLink: function () {
      drawLink(this);
    },
    drawImage: function () {
      drawImage(this);
    },
    drawTable: function () {
      drawTable(this);
    },
    drawHorizontalRule: function () {
      drawHorizontalRule(this);
    },
    undo: function () {
      undo(this);
    },
    redo: function () {
      redo(this);
    },
    togglePreview: function () {
      togglePreview(this);
    },
    toggleSideBySide: function () {
      toggleSideBySide(this);
    },
    toggleFullScreen: function () {
      toggleFullScreen(this);
    },
    isPreviewActive: function () {
      var cm = this.codemirror;
      var wrapper = cm.getWrapperElement();
      var preview = wrapper.lastChild;

      return /editor-preview-active/.test(preview.className);
    },
    isSideBySideActive: function () {
      var cm = this.codemirror;
      var wrapper = cm.getWrapperElement();
      var preview = wrapper.nextSibling;

      return /editor-preview-active-side/.test(preview.className);
    },
    isFullscreenActive: function () {
      var cm = this.codemirror;

      return cm.getOption("fullScreen");
    },
    getState: function () {
      var cm = this.codemirror;

      return getState(cm);
    },
    toTextArea: function () {
      var cm = this.codemirror;
      var wrapper = cm.getWrapperElement();
      if (wrapper.parentNode) {
        if (this.gui.toolbar) {
          wrapper.parentNode.removeChild(this.gui.toolbar);
        }
        if (this.gui.statusbar) {
          wrapper.parentNode.removeChild(this.gui.statusbar);
        }
        if (this.gui.sideBySide) {
          wrapper.parentNode.removeChild(this.gui.sideBySide);
        }
      }
      cm.toTextArea();
      if (this.autosaveTimeoutId) {
        clearTimeout(this.autosaveTimeoutId);
        this.autosaveTimeoutId = undefined;
        this.clearAutosavedValue();
      }
    },
    //初始化编辑器
    init: function (options) {
      // Handle options parameter
      options = options || {};
      // Used later to refer to it"s parent
      options.parent = this;

      // Find the textarea to use
      if (options.element) {
        this.element = options.element;
      } else if (options.element === null) {
        // This means that the element option was specified, but no element was found
        console.log("easymde: Error. No element was found.");
        return;
      }

      // Handle toolbar 未定义toolbar参数时，自动初始化
      if (options.toolbar === undefined) {
        // Initialize
        options.toolbar = [];
        // Loop over the built in buttons, to get the preferred order
        for (var key in toolbarBuiltInButtons) {
          if (toolbarBuiltInButtons.hasOwnProperty(key)) {
            if (key.indexOf("separator-") != -1) {
              options.toolbar.push("|");
            }
            if (
              toolbarBuiltInButtons[key].default === true ||
              (options.showIcons &&
                options.showIcons.constructor === Array &&
                options.showIcons.indexOf(key) != -1)
            ) {
              options.toolbar.push(key);
            }
          }
        }
      }

      // Handle status bar
      if (!options.hasOwnProperty("status")) {
        options.status = ["autosave", "lines", "words", "cursor"];
      }

      // Add default preview rendering function
      if (!options.previewRender) {
        options.previewRender = function (plainText) {
          // Note: "this" refers to the options object
          return this.parent.markdown(plainText);
        };
      }

      // Set default options for parsing config
      options.parsingConfig = extend(
        {
          highlightFormatting: true, // needed for toggleCodeBlock to detect types of code
        },
        options.parsingConfig || {}
      );

      // Merging the insertTexts, with the given options
      options.insertTexts = extend({}, insertTexts, options.insertTexts || {});

      // Merging the promptTexts, with the given options
      options.promptTexts = promptTexts;

      // Merging the blockStyles, with the given options
      options.blockStyles = extend({}, blockStyles, options.blockStyles || {});

      // Merging the shortcuts, with the given options
      options.shortcuts = extend({}, shortcuts, options.shortcuts || {});

      // Change unique_id to uniqueId for backwards compatibility
      if (
        options.autosave != undefined &&
        options.autosave.unique_id != undefined &&
        options.autosave.unique_id != ""
      )
        options.autosave.uniqueId = options.autosave.unique_id;

      // Update this options
      this.options = options;

      // Auto render
      this.render();

      // The codemirror component is only available after rendering
      // so, the setter for the initialValue can only run after
      // the element has been rendered
      if (
        options.initialValue &&
        (!this.options.autosave ||
          this.options.autosave.foundSavedValue !== true)
      ) {
        this.value(options.initialValue);
      }
    },
    markdown: function (text) {
      /**
       * Default markdown render.
       */
      if (marked) {
        // Initialize
        var markedOptions = {};

        // Update options
        if (
          this.options &&
          this.options.renderingConfig &&
          this.options.renderingConfig.singleLineBreaks === false
        ) {
          markedOptions.breaks = false;
        } else {
          markedOptions.breaks = true;
        }

        // if (
        //   this.options &&
        //   this.options.renderingConfig &&
        //   this.options.renderingConfig.codeSyntaxHighlighting === true &&
        //   window.hljs
        // ) {

        // }
        //代码高亮
        markedOptions.highlight = function (code) {
          return window.hljs.highlightAuto(code).value;
        };

        // Set options
        marked.setOptions(markedOptions);

        // Return
        return marked(text);
      }
    },
    render: function (el) {
      /**
       * Render editor to the given element.
       */

      if (!el) {
        el = this.element || document.getElementsByTagName("textarea")[0];
      }

      if (this._rendered && this._rendered === el) {
        // Already rendered.
        return;
      }

      this.element = el;
      var options = this.options;
      var shortcut = options.shortcut === false;
      //是否启用快捷键
      if (!shortcut) {
        var self = this;
        var keyMaps = {};
        for (var key in options.shortcuts) {
          // null stands for "do not bind this command"
          if (options.shortcuts[key] !== null && bindings[key] !== null) {
            (function (key) {
              keyMaps[fixShortcut(options.shortcuts[key])] = function () {
                bindings[key](self);
              };
            })(key);
          }
        }

        keyMaps["Enter"] = "newlineAndIndentContinueMarkdownList";
        keyMaps["Tab"] = "tabAndIndentMarkdownList";
        keyMaps["Shift-Tab"] = "shiftTabAndUnindentMarkdownList";
        keyMaps["Esc"] = function (cm) {
          if (cm.getOption("fullScreen")) toggleFullScreen(self);
        };

        document.addEventListener(
          "keydown",
          function (e) {
            e = e || window.event;

            if (e.keyCode == 27) {
              if (self.codemirror.getOption("fullScreen"))
                toggleFullScreen(self);
            }
          },
          false
        );
      }

      var mode, backdrop;
      if (options.spellChecker !== false) {
        mode = "spell-checker";
        backdrop = options.parsingConfig;
        backdrop.name = "gfm";
        backdrop.gitHubSpice = false;

        CodeMirrorSpellChecker({
          codeMirrorInstance: CodeMirror,
        });
      } else {
        mode = options.parsingConfig;
        mode.name = "gfm";
        mode.gitHubSpice = false;
      }

      this.codemirror = CodeMirror.fromTextArea(el, {
        mode: mode,
        backdrop: backdrop,
        theme: "paper",
        tabSize: options.tabSize != undefined ? options.tabSize : 2,
        indentUnit: options.tabSize != undefined ? options.tabSize : 2,
        indentWithTabs: options.indentWithTabs === false ? false : true,
        lineNumbers: false,
        autofocus: options.autofocus === true ? true : false,
        extraKeys: keyMaps,
        lineWrapping: options.lineWrapping === false ? false : true,
        allowDropFileTypes: ["text/plain"],
        placeholder:
          options.placeholder || el.getAttribute("placeholder") || "",
        styleSelectedText:
          options.styleSelectedText != undefined
            ? options.styleSelectedText
            : true,
      });

      if (options.forceSync === true) {
        var cm = this.codemirror;
        cm.on("change", function () {
          cm.save();
        });
      }

      this.gui = {};

      if (options.toolbar !== false) {
        this.gui.toolbar = this.createToolbar();
      }
      if (options.status !== false) {
        this.gui.statusbar = this.createStatusbar();
      }
      if (options.autosave != undefined && options.autosave.enabled === true) {
        this.autosave();
      }

      this.gui.sideBySide = this.createSideBySide();

      this._rendered = this.element;

      // Fixes CodeMirror bug (#344)
      var temp_cm = this.codemirror;
      setTimeout(
        function () {
          temp_cm.refresh();
        }.bind(temp_cm),
        0
      );
    },
    autosave: function () {
      if (isLocalStorageAvailable()) {
        var easymde = this;

        //不存在uniqueId，使用默认uniqueId：LayJAVA
        if (!this.options.autosave.uniqueId) {
          this.options.autosave.uniqueId == "LayJAVA";
        }

        if (easymde.element.form != null && easymde.element.form != undefined) {
          easymde.element.form.addEventListener("submit", function () {
            localStorage.removeItem(
              "smde_" + easymde.options.autosave.uniqueId
            );
          });
        }

        if (this.options.autosave.loaded !== true) {
          if (
            typeof localStorage.getItem(
              "smde_" + this.options.autosave.uniqueId
            ) == "string" &&
            localStorage.getItem("smde_" + this.options.autosave.uniqueId) != ""
          ) {
            this.codemirror.setValue(
              localStorage.getItem("smde_" + this.options.autosave.uniqueId)
            );
            this.options.autosave.foundSavedValue = true;
          }

          this.options.autosave.loaded = true;
        }

        localStorage.setItem(
          "smde_" + this.options.autosave.uniqueId,
          easymde.value()
        );

        var el = document.getElementById("autosaved");
        if (el != null && el != undefined && el != "") {
          var d = new Date();
          var hh = d.getHours();
          var m = d.getMinutes();
          var dd = "上午";
          var h = hh;
          if (h >= 12) {
            h = hh - 12;
            dd = "下午";
          }
          if (h == 0) {
            h = 12;
          }
          m = m < 10 ? "0" + m : m;

          el.innerHTML = "已自动保存: " + dd + " " + h + ":" + m;
        }

        this.autosaveTimeoutId = setTimeout(function () {
          easymde.autosave();
        }, this.options.autosave.delay || 5000);
      } else {
        console.log("easymde: localStorage not available, cannot autosave");
      }
    },
    clearAutosavedValue: function () {
      if (isLocalStorageAvailable()) {
        if (
          this.options.autosave == undefined ||
          this.options.autosave.uniqueId == undefined ||
          this.options.autosave.uniqueId == ""
        ) {
          console.log(
            "easymde: You must set a uniqueId to clear the autosave value"
          );
          return;
        }

        localStorage.removeItem("smde_" + this.options.autosave.uniqueId);
      } else {
        console.log("easy'm'de: localStorage not available, cannot autosave");
      }
    },
    createSideBySide: function () {
      var cm = this.codemirror;
      var wrapper = cm.getWrapperElement();
      var preview = wrapper.nextSibling;

      if (!preview || !/editor-preview-side/.test(preview.className)) {
        preview = document.createElement("div");
        preview.className = "editor-preview-side markdown-body";
        wrapper.parentNode.insertBefore(preview, wrapper.nextSibling);
      }

      // Syncs scroll  editor -> preview
      var cScroll = false;
      var pScroll = false;
      cm.on("scroll", function (v) {
        if (cScroll) {
          cScroll = false;
          return;
        }
        pScroll = true;
        var height = v.getScrollInfo().height - v.getScrollInfo().clientHeight;
        var ratio = parseFloat(v.getScrollInfo().top) / height;
        var move = (preview.scrollHeight - preview.clientHeight) * ratio;
        preview.scrollTop = move;
      });

      // Syncs scroll  preview -> editor
      preview.onscroll = function () {
        if (pScroll) {
          pScroll = false;
          return;
        }
        cScroll = true;
        var height = preview.scrollHeight - preview.clientHeight;
        var ratio = parseFloat(preview.scrollTop) / height;
        var move =
          (cm.getScrollInfo().height - cm.getScrollInfo().clientHeight) * ratio;
        cm.scrollTo(0, move);
      };
      return preview;
    },
    createToolbar: function (items) {
      items = items || this.options.toolbar;

      if (!items || items.length === 0) {
        return;
      }
      var i;
      for (i = 0; i < items.length; i++) {
        if (toolbarBuiltInButtons[items[i]] != undefined) {
          items[i] = toolbarBuiltInButtons[items[i]];
        }
      }

      var bar = document.createElement("div");
      bar.className = "editor-toolbar";

      var self = this;

      var toolbarData = {};
      self.toolbar = items;

      for (i = 0; i < items.length; i++) {
        if (items[i].name == "guide" && self.options.toolbarGuideIcon === false)
          continue;

        if (
          self.options.hideIcons &&
          self.options.hideIcons.indexOf(items[i].name) != -1
        )
          continue;

        // Fullscreen does not work well on mobile devices (even tablets)
        // In the future, hopefully this can be resolved
        if (
          (items[i].name == "fullscreen" || items[i].name == "side-by-side") &&
          isMobile()
        )
          continue;

        // Don't include trailing separators
        if (items[i] === "|") {
          var nonSeparatorIconsFollow = false;

          for (var x = i + 1; x < items.length; x++) {
            if (
              items[x] !== "|" &&
              (!self.options.hideIcons ||
                self.options.hideIcons.indexOf(items[x].name) == -1)
            ) {
              nonSeparatorIconsFollow = true;
            }
          }

          if (!nonSeparatorIconsFollow) continue;
        }

        // Create the icon and append to the toolbar
        (function (item) {
          var el;
          if (item === "|") {
            el = createSep();
          } else {
            el = createIcon(
              item,
              self.options.toolbarTips,
              self.options.shortcuts
            );
          }

          // bind events, special for info
          if (item.action) {
            if (typeof item.action === "function") {
              el.onclick = function (e) {
                e.preventDefault();
                item.action(self);
              };
            } else if (typeof item.action === "string") {
              el.href = item.action;
              el.target = "_blank";
            }
          }

          toolbarData[item.name || item] = el;
          bar.appendChild(el);
        })(items[i]);
      }

      self.toolbarElements = toolbarData;

      var cm = this.codemirror;
      cm.on("cursorActivity", function () {
        var stat = getState(cm);

        for (var key in toolbarData) {
          (function (key) {
            var el = toolbarData[key];
            if (stat[key]) {
              el.className += " active";
            } else if (key != "fullscreen" && key != "side-by-side") {
              el.className = el.className.replace(/\s*active\s*/g, "");
            }
          })(key);
        }
      });

      var cmWrapper = cm.getWrapperElement();
      cmWrapper.parentNode.insertBefore(bar, cmWrapper);
      return bar;
    },
    //创建状态栏
    createStatusbar: function (status) {
      // Initialize
      status = status || this.options.status;
      var options = this.options;

      //是否显示状态栏
      var show = options.autosave.show === false;
      if (show) {
        return;
      }

      var cm = this.codemirror;
      // Make sure the status variable is valid
      if (!status || status.length === 0) return;

      // Set up the built-in items
      var items = [];
      var i, onUpdate, defaultValue;

      for (i = 0; i < status.length; i++) {
        // Reset some values
        onUpdate = undefined;
        defaultValue = undefined;

        // Handle if custom or not
        if (typeof status[i] === "object") {
          items.push({
            className: status[i].className,
            defaultValue: status[i].defaultValue,
            onUpdate: status[i].onUpdate,
          });
        } else {
          var name = status[i];

          if (name === "words") {
            defaultValue = function (el) {
              el.innerHTML = wordCount(cm.getValue());
            };
            onUpdate = function (el) {
              el.innerHTML = wordCount(cm.getValue());
            };
          } else if (name === "lines") {
            defaultValue = function (el) {
              el.innerHTML = cm.lineCount();
            };
            onUpdate = function (el) {
              el.innerHTML = cm.lineCount();
            };
          } else if (name === "cursor") {
            defaultValue = function (el) {
              el.innerHTML = "光标: 0行0列";
            };
            onUpdate = function (el) {
              var pos = cm.getCursor();
              el.innerHTML = "光标: " + pos.line + "行" + pos.ch + "列";
            };
          } else if (name === "autosave") {
            defaultValue = function (el) {
              //支持autosave参数设置为boolean类型
              if (options.autosave === true) {
                options.autosave = { enabled: true };
              }
              if (
                options.autosave != undefined &&
                options.autosave.enabled === true
              ) {
                el.setAttribute("id", "autosaved");
              }
            };
          }

          items.push({
            className: name,
            defaultValue: defaultValue,
            onUpdate: onUpdate,
          });
        }
      }

      // Create element for the status bar
      var bar = document.createElement("div");
      bar.className = "editor-statusbar";
      // Create a new span for each item
      for (i = 0; i < items.length; i++) {
        // Store in temporary variable
        var item = items[i];

        // Create span element
        var el = document.createElement("span");
        el.className = item.className;

        // Ensure the defaultValue is a function
        if (typeof item.defaultValue === "function") {
          item.defaultValue(el);
        }

        // Ensure the onUpdate is a function
        if (typeof item.onUpdate === "function") {
          // Create a closure around the span of the current action, then execute the onUpdate handler
          this.codemirror.on(
            "update",
            (function (el, item) {
              return function () {
                item.onUpdate(el);
              };
            })(el, item)
          );
        }

        // Append the item to the status bar
        bar.appendChild(el);
      }

      // Insert the status bar into the DOM
      var cmWrapper = this.codemirror.getWrapperElement();
      cmWrapper.parentNode.insertBefore(bar, cmWrapper.nextSibling);
      return bar;
    },
    /**
     * Get or set the text content.
     */
    value: function (val) {
      if (val === undefined) {
        return this.codemirror.getValue();
      } else {
        this.codemirror.getDoc().setValue(val);
        return this;
      }
    },
    html: function (text) {
		if (text === undefined) {
			return this.markdown(this.value());
		}
    return this.markdown(text);
    },
  };

  //easymde接口
  exports("easymde", easymde);
});
